/*
----------------------------------------------------------------------------------------------------
This source file is part of the Gamut Framework
Copyright (c) 2007 by Ryan Holcombe

This program is free software; you can redistribute it and/or modify it under the terms of the GNU
Lesser General Public License as published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
----------------------------------------------------------------------------------------------------
*/


#include "GamutLogFile.h"
#include "GamutException.h"
#include <sstream>
#include <iomanip>
#include <time.h>

#ifdef WIN32
	#include <direct.h>
#endif

#ifdef __linux__
	#include <sys/stat.h>
	#include <errno.h>
#endif

namespace Gamut
{
	// -------------------------------------------------------------------------------------------//
	LogFile::LogFile(const std::string& name, const std::string& log, const std::string& dir) : 
		mEntryCnt(0), mLogFile(log), mLogName(name), mLogDir(dir)
	{
		// get the name of the xsl file
		int loc = mLogFile.rfind('.');
		if (loc == std::string::npos)
			mXslFile = mLogFile + ".xsl";
		else
		{
			mXslFile = mLogFile;
			mXslFile.replace(loc, mXslFile.length(), ".xsl");
		}
		
		// add the '/' at the end of the dir name if it does not already exist
		if (mLogDir.at(mLogDir.length()-1) != '/')
			mLogDir.push_back('/');
	}
	
	// -------------------------------------------------------------------------------------------//
	void LogFile::onListenerDestroy()
	{
		if (mFile.is_open())
			mFile.close();
	}
	
	// -------------------------------------------------------------------------------------------//
	void LogFile::onListenerAdd()
	{
		time_t rawtime;
		struct tm *t;
		std::stringstream logdate, logtime;
	
#ifdef WIN32
		// mkdir using windows _mkdir
#endif

#ifdef __linux__
		if (mkdir(mLogDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == -1)
		{
			// throw exception if any error received other than 'DIR EXISTS'
			if (errno != EEXIST)
				throw Exception("LogFile::onListenerAdd", "Unable to create log directory " + mLogDir + " for log files");
		}
#endif
		
		generateXSL();
		
		// create the log file
		mFile.open((mLogDir + mLogFile).c_str(), std::ios_base::out);
		
		if (!mFile.is_open())
			throw Exception("LogFile::onListenerAdd", "Unable to open the log file for writing: " + mLogDir + mLogFile);
		
		// get the current date/time	
		rawtime = time(NULL);
		t = localtime(&rawtime);
		logdate << t->tm_mon+1 << "/" << t->tm_mday << "/" << t->tm_year+1900;
		logtime << std::setw(2) << std::setfill('0') << t->tm_hour << ":"
				<< std::setw(2) << std::setfill('0') << t->tm_min << ":"
				<< std::setw(2) << std::setfill('0') << t->tm_sec;
				
		// write the log headers
		mFile << "<?xml version=\"1.0\"?>" << std::endl;
		mFile << "<?xml-stylesheet type=\"text/xsl\" href=\"" << mXslFile << "\" ?>" << std::endl;
		mFile << "<RuntimeLog>" << std::endl;
		mFile << "\t<LogHeader>" << std::endl;
		mFile << "\t\t<LogTitle>" << mLogName << "</LogTitle>" << std::endl;
		mFile << "\t\t<LogDate>" << logdate.str() << "</LogDate>" << std::endl;
		mFile << "\t\t<LogTime>" << logtime.str() << "</LogTime>" << std::endl;
		mFile << "\t</LogHeader>" << std::endl;
		mFile << "</RuntimeLog>" << std::endl;
	}
	
	// -------------------------------------------------------------------------------------------//
	void LogFile::writeEntry(const std::string& msg, const std::string& file, int line,
							 const std::string& time, const std::string& level)
	{
		if (!mFile.is_open())
			return;

		// exceptions do not include a line number
		std::stringstream lineStream;
		if (line == 0)
			lineStream << "??";
		else
			lineStream << line;
		
		// the previous xml entry has been properly closed. write a new entry, and reclose
		long pos=mFile.tellp();

		// make sure we aren't at the beginning of the file
		if (pos-14 < 0)
			throw Exception("LogFile::writeEntry", "Log File cannot be written to, check permissions");

		mFile.seekp(pos-14);
		
		mFile << "\t<LogEntry id=\"" << mEntryCnt++ << "\">" << std::endl;
		mFile << "\t\t<Level>" << level << "</Level>" << std::endl;
		mFile << "\t\t<Msg>" << msg << "</Msg>" << std::endl;
		mFile << "\t\t<File>" << file << "</File>" << std::endl;
		mFile << "\t\t<Line>" << lineStream.str() << "</Line>" << std::endl;
		mFile << "\t\t<Time>" << time << "</Time>" << std::endl;
		mFile << "\t</LogEntry>" << std::endl;
		mFile << "</RuntimeLog>" << std::endl;	
	}
	
	// -------------------------------------------------------------------------------------------//
	void LogFile::generateXSL() const
	{		
		std::string xsl = "\n\
		<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n\n\
		<!-- This document was originally created by: Jack \"jollyjeffers\" Hoxley and Oli \"evolutional\" Wilkinson -->\n\
		<!-- The original article can be found at: http://www.gamedev.net/reference/programming/features/xmltech/ -->\n\
		<!-- Modified to fit the X-Log Library by Nick \"Odis\" Cash -->\n\n\
		<!-- This following section is the entry point for parsing the associated XML        -->\n\
		<!-- document. It hands out parsing to the relevent subsections and generally links  -->\n\
			<xsl:template match=\"/\">\n\n\
				<html>\n\
					<head>\n\
						<title>Log File Viewer</title>\n\
					</head>\n\
					<body>\n\
							<xsl:apply-templates select=\"RuntimeLog/LogHeader\"/>\n\
						<br/>\n\
						<br/>\n\
						<b>\n\
							<font face=\"Arial\" size=\"3\" color=\"#000000\">\n\
								Details of selected log entries:\n\
							</font>\n\
						</b>\n\
						<br/>\n\
						<br/>\n\
						<table border=\"1\" width=\"100%%\" cellspacing=\"0\" cellpadding=\"0\" bordercolorlight=\"#000000\" bordercolordark=\"#ffffff\" bordercolor=\"#000000\">\n\
							<tr>\n\
								<td width=\"10%%\"  bgcolor=\"#000000\"><font size=\"2\" face=\"Arial\" color=\"#FFFFFF\"><b><center>#</center></b></font></td>\n\
								<td width=\"25%%\" bgcolor=\"#000000\"><font size=\"2\" face=\"Arial\" color=\"#FFFFFF\"><b><center>Time</center></b></font></td>\n\
								<td width=\"30%%\" bgcolor=\"#000000\"><font size=\"2\" face=\"Arial\" color=\"#FFFFFF\"><b><center>File/Function</center></b></font></td>\n\
								<td width=\"20%%\" bgcolor=\"#000000\"><font size=\"2\" face=\"Arial\" color=\"#FFFFFF\"><b><center>Level</center></b></font></td>\n\
								<td width=\"15%%\"  bgcolor=\"#000000\"><font size=\"2\" face=\"Arial\" color=\"#FFFFFF\"><b><center>Line</center></b></font></td>\n\
							</tr>\n\
							<xsl:apply-templates select=\"RuntimeLog/LogEntry\"/>\n\
						</table>\n\
		\n\
					</body>\n\
				</html>\n\
		\n\
			</xsl:template>\n\
		\n\
		\n\
		\n\
		<!-- This next template match is for the log header, that is the general session     -->\n\
		<!-- information and run-time system statistics/configuration                        -->\n\
			<xsl:template match=\"LogHeader\">\n\
		\n\
				<font face=\"Arial\" size=\"5\" color=\"#2060AA\">\n\
					<b>\n\
						<u>\n\
							<xsl:value-of select=\"LogTitle\"/>\n\
						</u>\n\
					</b>\n\
				</font>\n\
				<br/>\n\
				<br/>\n\
				<b>\n\
					<font face=\"Arial\" size=\"3\" color=\"#000000\">\n\
						File information:\n\
					</font>\n\
				</b>\n\
				<br/>\n\
				<font face=\"Arial\" size=\"2\" color=\"#000000\">\n\
					Date:\n\
				</font>\n\
				<i>\n\
					<font face=\"Arial\" size=\"2\" color=\"#808080\">\n\
						<xsl:value-of select=\"LogDate\"/>\n\
					</font>\n\
				</i>\n\
				<br/>\n\
				<font face=\"Arial\" size=\"2\" color=\"#000000\">\n\
					This log file was created at:\n\
				</font>\n\
				<i>\n\
					<font face=\"Arial\" size=\"2\" color=\"#808080\">\n\
						<xsl:value-of select=\"LogTime\"/>\n\
					</font>\n\
				</i>\n\
				<br/>\n\
				<font face=\"Arial\" size=\"2\" color=\"#000000\">\n\
					Total logged events:\n\
				</font>\n\
				<i>\n\
					<font face=\"Arial\" size=\"2\" color=\"#808080\">\n\
						<xsl:copy-of select=\"count(../LogEntry)\"/>\n\
					</font>\n\
				</i>\n\
		\n\
			</xsl:template>\n\
		\n\
		\n\
		\n\
		<!-- This template match is for each log entry, filtered according to the selected   -->\n\
		<!-- type of event filter. For each log entry we add a row to the table.             -->\n\
				<xsl:template match=\"LogEntry\">\n\
				  <xsl:choose>\n\
						<xsl:when test=\"Level=\'Normal\'\">\n\
									<tr bgcolor=\"#80FF80\" valign=\"middle\" align=\"center\">\n\
		\n\
										<td>\n\
											<font size=\"2\" face=\"Arial\" color=\"#202020\">\n\
												<center>\n\
													<xsl:value-of select=\"@id\"/>\n\
												</center>\n\
											</font>\n\
										</td>\n\
		\n\
										<xsl:apply-templates select=\"Time\"/>\n\
										<xsl:apply-templates select=\"File\"/>\n\
										<xsl:apply-templates select=\"Level\"/>\n\
										<xsl:apply-templates select=\"Line\"/>\n\
		\n\
									</tr>\n\
									<tr bgcolor=\"#AAFFAA\">\n\
										<xsl:apply-templates select=\"Msg\"/>\n\
									</tr>\n\
						</xsl:when>\n\
						<xsl:when test=\"Level=\'Informational\'\">\n\
									<tr bgcolor=\"#EEEEEE\" valign=\"middle\" align=\"center\">\n\
		\n\
										<td>\n\
											<font size=\"2\" face=\"Arial\" color=\"#202020\">\n\
												<center>\n\
													<xsl:value-of select=\"@id\"/>\n\
												</center>\n\
											</font>\n\
										</td>\n\
		\n\
										<xsl:apply-templates select=\"Time\"/>\n\
										<xsl:apply-templates select=\"File\"/>\n\
										<xsl:apply-templates select=\"Level\"/>\n\
										<xsl:apply-templates select=\"Line\"/>\n\
		\n\
									</tr>\n\
									<tr bgcolor=\"#AAAAAA\">\n\
										<xsl:apply-templates select=\"Msg\"/>\n\
									</tr>\n\
						</xsl:when>\n\
						<xsl:when test=\"Level=\'Exception\'\">\n\
									<tr bgcolor=\"#FF8080\" valign=\"middle\" align=\"center\">\n\
		\n\
										<td>\n\
											<font size=\"2\" face=\"Arial\" color=\"#202020\">\n\
												<center>\n\
													<xsl:value-of select=\"@id\"/>\n\
												</center>\n\
											</font>\n\
										</td>\n\
		\n\
										<xsl:apply-templates select=\"Time\"/>\n\
										<xsl:apply-templates select=\"File\"/>\n\
										<xsl:apply-templates select=\"Level\"/>\n\
										<xsl:apply-templates select=\"Line\"/>\n\
		\n\
									</tr>\n\
									<tr bgcolor=\"#FFAAAA\">\n\
										<xsl:apply-templates select=\"Msg\"/>\n\
									</tr>\n\
						</xsl:when>\n\
						<xsl:when test=\"Level='Warning'\">\n\
									<tr bgcolor=\"#FFAA80\" valign=\"middle\" align=\"center\">\n\
		\n\
										<td>\n\
											<font size=\"2\" face=\"Arial\" color=\"#202020\">\n\
												<center>\n\
													<xsl:value-of select=\"@id\"/>\n\
												</center>\n\
											</font>\n\
										</td>\n\
		\n\
										<xsl:apply-templates select=\"Time\"/>\n\
										<xsl:apply-templates select=\"File\"/>\n\
										<xsl:apply-templates select=\"Level\"/>\n\
										<xsl:apply-templates select=\"Line\"/>\n\
		\n\
									</tr>\n\
									<tr bgcolor=\"#FFDDAA\">\n\
										<xsl:apply-templates select=\"Msg\"/>\n\
									</tr>\n\
						</xsl:when>\n\
					</xsl:choose>\n\
		\n\
			</xsl:template>\n\
		\n\
		\n\
		\n\
		<!-- This match is for the formatted time output associate with each log entry       -->\n\
			<xsl:template match=\"Time\">\n\
				<td>\n\
					<font size=\"2\" face=\"Courier New\" color=\"#404040\">\n\
						<center>\n\
							<xsl:apply-templates/>\n\
						</center>\n\
					</font>\n\
				</td>\n\
			</xsl:template>\n\
		\n\
		\n\
		\n\
		<!-- This match is for the source code filename where this entry originated from     -->\n\
			<xsl:template match=\"File\">\n\
				<td>\n\
					<font size=\"2\" face=\"Courier New\" color=\"#404040\">\n\
						<xsl:apply-templates/>\n\
					</font>\n\
				</td>\n\
			</xsl:template>\n\
		\n\
		\n\
		\n\
		<!-- This outputs the line number for the given source code file.                    -->\n\
			<xsl:template match=\"Level\">\n\
				<td>\n\
					<font size=\"2\" face=\"Courier New\" color=\"#404040\">\n\
						<center>\n\
							<xsl:apply-templates/>\n\
						</center>\n\
					</font>\n\
				</td>\n\
			</xsl:template>\n\
		\n\
		\n\
		\n\
		<!-- This outputs the line number for the given source code file.                    -->\n\
			<xsl:template match=\"Line\">\n\
				<td>\n\
					<font size=\"2\" face=\"Courier New\" color=\"#404040\">\n\
						<center>\n\
							<xsl:apply-templates/>\n\
						</center>\n\
					</font>\n\
				</td>\n\
			</xsl:template>\n\
		\n\
		\n\
		\n\
		<!-- This match outputs the raw 'Message' data that's attached to this log entry.    -->\n\
			<xsl:template match=\"Msg\">\n\
				<td bgcolor=\"#FFFFFF\">\n\
				</td>\n\
					<td colspan=\"4\">\n\
						<font size=\"2\" face=\"Arial\" color=\"#000000\">\n\
							<b>\n\
								<i>\n\
									<xsl:apply-templates/>\n\
								</i>\n\
							</b>\n\
						</font>\n\
					</td>\n\
			</xsl:template>\n\
		\n\
		\n\
		\n\
		</xsl:stylesheet>\n\
		";
		
		// check to see if the file exists by opening it
		std::ifstream fs((mLogDir + mXslFile).c_str());
		if (!fs.is_open())
		{
			// open the file and write if the file does not exist
			std::ofstream fout((mLogDir + mXslFile).c_str(), std::ios::out);
			fout << xsl;
			fout.close();
		}
	}
}
